
#include "samplers.fx"
#include "shading.fx"

static const float3 COLORS[10] = { float3(1.5,0.8,0.2), float3(1,1,1), float3(1,0,0), float3(0,1,0), float3(0,0,1), float3(1,1,0), float3(1,0,1), float3(0,1,1), float3(1,0.5,0), float3(1,0,0.5) };
static const uint NUM_COLORS = 10;

struct VS_IN
{
	float3	position	:	POSITION;
	float3	normal		:	NORMAL;
	float3	tangent		:	TANGENT;
	float2	tex			:	TEX_COORDS;
	uint InstanceId		:	SV_InstanceID;
};
//____________
struct ControlPoint
{
	float3	position	:	POSITION;
	uint3	instData	:   INSTANCE;
};
//____________
struct quadTessFactors
{
	float	edge[4]		:	SV_TessFactor;
	float	inside[2]	:	SV_InsideTessFactor;
	float3  color		:   COLOR;
};
//____________


struct DS_OUT
{
	float4	position	:	SV_POSITION;
	float3  wNormal		:	NORMAL;
	float2  tex			:	COORDS;
	float	h			:	HEIGHT;
};

//________ V A R I A B L E S____________________________________________________________________________

Texture2D	texTerrainHeightmap;
Texture2D	texTerrainNormalmap;
float4x4	m_World;
float4x4	m_WorldViewProj;
float		fInsideTess;
float		fEdgeTess;
uint		iNumPatch;
float		fPatchSize;
float		fWidth;
bool		bViewAdaptiveTess;
bool		bOn;
float3		vEyePos;
float		fHeightController;
float		fMinTessFactor;
float		fMaxTessFactor;
float		fMaxViewDist;
float		fMinViewDist;
bool		bDebug;

static const float fSize = 1.0f;

float3 arrControl_Points[4]  = {	float3(-fSize,0,fSize),	 float3(fSize,0,fSize),
									float3(-fSize,0,-fSize), float3(fSize,0,-fSize)
								};

//________ F U N C T I O N S________________________________________________________________________________



float HeightMap(float2 uv)
{


	float Bx=smoothstep(0.3, 0.5, uv.x)-smoothstep(0.5, 0.7, uv.x);
	float By=smoothstep(0.3, 0.5, uv.y)-smoothstep(0.5, 0.7, uv.y);

	return lerp(lerp(Bx,By, uv.x), lerp(Bx,By, uv.y), uv.x*uv.y);
}



//________ S H A D E R S________________________________________________________________________________


ControlPoint VS_BezSurface(VS_IN vin)
{
	ControlPoint vout = (ControlPoint)0;

	if (!bViewAdaptiveTess)
		vout.position = vin.position;
	else {
		uint row = vin.InstanceId / iNumPatch;
		uint col = vin.InstanceId % iNumPatch;

		float start = -fWidth/2.0f;

		float2 cntrCoords = float2(start + row*fPatchSize + fPatchSize*0.5f, 
							      -start - col*fPatchSize - fPatchSize*0.5f);

		vout.position = float3(cntrCoords.x, vin.position.y, cntrCoords.y);
		vout.instData = uint3(row, col, vin.InstanceId);
	}

	return vout;
}



//_______________
											// ctrl points per patch
quadTessFactors ConstantHS_BezSurface(InputPatch<ControlPoint, 1> patch, uint patchID : SV_PrimitiveID) // Constant Hull Shader
{
	
	quadTessFactors tout;

	float max_distance = fMaxViewDist;
				
				
	uint tess_factor;
	float d = length(patch[0].position - vEyePos);
	d = clamp(d, 0.0f, max_distance);
	
	uint min_tess_factor = fMinTessFactor;
	uint max_tess_factor = fMaxTessFactor;

	tess_factor = (uint)(min_tess_factor + (1.0f-d/max_distance) * (max_tess_factor - min_tess_factor)); 
	
			
	float view_perc = saturate((fMaxViewDist-d) / (fMaxViewDist-fMinViewDist)); //[0: far, 1:near]
	int tessFactor = (int)lerp(fMinTessFactor, fMaxTessFactor, view_perc);
			
			
	tout.edge[0] = tessFactor;
	tout.edge[1] = tessFactor;
	tout.edge[2] = tessFactor;
	tout.edge[3] = tessFactor;
				
	tout.inside[0] = tessFactor;
	tout.inside[1] = tessFactor;
			
	tout.color = COLORS[patch[0].instData.z % (NUM_COLORS-1)];
			
	return tout;
}
//_______________

[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("ConstantHS_BezSurface")]
[maxtessfactor(64.0f)]
ControlPoint ControlPointHS_BezSurface_Integer(InputPatch<ControlPoint, 1> patch,	   uint ctrlPoint_index : SV_OutputControlPointID, 
																		       uint patchId	     	: SV_PrimitiveID)
{

	// Output 4x4 control points
	ControlPoint p;
	p.position = patch[0].position + arrControl_Points[ctrlPoint_index] * fPatchSize* 0.5f;
	p.instData = patch[0].instData;
	return p;
}
//_______________

[domain("quad")]
[partitioning("fractional_even")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("ConstantHS_BezSurface")]
[maxtessfactor(64.0f)]
ControlPoint ControlPointHS_BezSurface_FracEven(InputPatch<ControlPoint, 1> patch,	   uint ctrlPoint_index : SV_OutputControlPointID, 
																		       uint patchId	     	: SV_PrimitiveID)
{

	// Output 4x4 control points
	ControlPoint p;
	p.position = patch[0].position + arrControl_Points[ctrlPoint_index] * fPatchSize* 0.5f;
	p.instData = patch[0].instData;
	return p;
}
//_______________

[domain("quad")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("ConstantHS_BezSurface")]
[maxtessfactor(64.0f)]
ControlPoint ControlPointHS_BezSurface_FracOdd(InputPatch<ControlPoint, 1> patch,	   uint ctrlPoint_index : SV_OutputControlPointID, 
																		       uint patchId	     	: SV_PrimitiveID)
{

	// Output 4x4 control points
	ControlPoint p;
	p.position = patch[0].position + arrControl_Points[ctrlPoint_index] * fPatchSize* 0.5f;
	p.instData = patch[0].instData;
	return p;
}
//_______________



[domain("quad")]
DS_OUT	DS_BezSurface(quadTessFactors tessFactors,	float2 coords : SV_DomainLocation, 
													const OutputPatch<ControlPoint, 4> patch)
{
	DS_OUT dout;
	

	float3 pos = (1.0f-coords.y) * ((1.0f-coords.x) * patch[0].position + coords.x * patch[1].position) +
					   coords.y  * ((1.0f-coords.x) * patch[2].position + coords.x * patch[3].position);
	

	float uv_interval = 1.0f / iNumPatch;
	float2 global_coords = float2(	patch[0].instData.x*uv_interval + coords.x * uv_interval, 
									patch[0].instData.y*uv_interval + coords.y * uv_interval);


	float h = 0.0f;

	h = texTerrainHeightmap.SampleLevel(s_linear, global_coords, 0).x;

	float minHeight = 2.0f;
	float maxHeight = 20.0f;
	float height = lerp(minHeight, maxHeight, fHeightController);

	pos.y += h * height;
		

	// position
	dout.position = mul(float4(pos,1.0f), m_WorldViewProj);
	// normal
	dout.wNormal = texTerrainNormalmap.SampleLevel(s_linear, global_coords, 0).x;

	dout.tex = coords;
	dout.h = h;

	
	if (bDebug)
		dout.wNormal = tessFactors.color;

	return dout;
}

//_______________


float3 terrainColor(float h)
{
	float3 brown = float3(0.55, 0.35, 0.16);
	float3 green = float3(0, 0.4, 0);

	return green * (1.0f - h) + brown * h;
}


float4 PS_BezSurface(DS_OUT pin) : SV_TARGET
{
	if (bDebug)
		return float4(pin.wNormal, 1.0f);

	float3 c = terrainColor(pin.h);
	float s = shade(pin.wNormal).x;

	return float4(c*s, 1);
}


//###################################################################################



DS_OUT VS_NoTess(VS_IN vin)
{
	DS_OUT dout = (DS_OUT)0;
	dout.position = mul(float4(vin.position, 1.0f), m_WorldViewProj);
	return dout;

}

float4 PS_NoTess(DS_OUT pin) : SV_TARGET
{
	return float4(1,0,0,1);
}

//###################################################################################


RasterizerState rs_Wireframe
{
	CullMode = Back;
	FillMode = Wireframe;
};


technique11 TessBezierSurface_Integer
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_BezSurface()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_BezSurface_Integer() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_BezSurface() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_5_0,   PS_BezSurface()   ) );
	}
}

technique11 TessBezierSurface_FracEven
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_BezSurface()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_BezSurface_FracEven() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_BezSurface() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_5_0,   PS_BezSurface()   ) );
	}
}

technique11 TessBezierSurface_FracOdd
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_BezSurface()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_BezSurface_FracOdd() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_BezSurface() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_5_0,   PS_BezSurface()   ) );
	}
}


technique11 NoTess
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_NoTess()   ) );
		SetPixelShader( CompileShader( ps_5_0,   PS_NoTess()   ) );
		
		SetRasterizerState(rs_Wireframe);
	}


}